# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import time
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from pydantic import TypeAdapter
from tortoise.queryset import Q
from tortoise.transactions import in_transaction

from common.utils.times import TimeUtil
from hypertext import PagingResult
from exception import AppException
from common.utils.urls import UrlUtil
from common.enums.pay import PayEnum
from common.enums.wallet import WalletEnum
from common.models.users import UserModel
from common.models.users import UserSubscribeModel
from common.models.users import UserWalletModel
from common.models.market import SubscribeOrderModel
from common.service.refund_logic import RefundLogic
from apps.admin.schemas.finance import subscribe_schema as schema


class SubscribeService:

    @classmethod
    async def lists(cls, params: schema.SubscribeSearchIn) -> PagingResult[schema.SubscribeListVo]:
        """
        订阅记录列表。

        Args:
            params (schema.SubscribeSearchIn): 订阅记录查询参数。

        Returns:
            PagingResult[schema.SubscribeListVo]: 订阅记录分页列表Vo。

        Author:
            zero
        """
        where = SubscribeOrderModel.build_search({
            "=": ["pay_way", "source_client"],
            "%like%": ["order_sn"],
            "datetime": ["start_time|end_time@create_time"]
        }, params.__dict__)
        where.append(Q(pay_status=PayEnum.PAID_OK))

        if params.user:
            user = await (UserModel
                          .filter(is_delete=0)
                          .filter(Q(sn=params.user) | Q(mobile=params.user) | Q(nickname=params.user))
                          .limit(310)
                          .values("id"))

            user_ids = [item["id"] for item in user if item["id"]] or [0]
            where.append(Q(user_id__in=list(set(user_ids))))

        _model = SubscribeOrderModel.filter(*where).order_by("-id")
        _pager = await SubscribeOrderModel.paginate(
            model=_model,
            page_no=params.page_no,
            page_size=params.page_size,
            datetime_field=["create_time", "pay_time", "package_end_time"]
        )

        user_ids = [item["user_id"] for item in _pager.lists if item["user_id"]]
        users: dict = await UserModel.queryUserDictsById(user_ids)

        list_vo = []
        for item in _pager.lists:
            user = users.get(item["user_id"]) or {}
            pack = json.loads(item["snapshot"] or "{}")

            item["package_name"] = pack.get("name") or "-"
            item["give_points"] = pack.get("give_points") or 0
            item["calendar"] = pack.get("calendar") or ""
            item["duration"] = pack.get("duration") or 0
            item["package_end_time"] = item["package_end_time"] or "-"
            item["pay_way"] = PayEnum.get_pay_way_msg(item["pay_way"])
            item["create_time"] = item["create_time"] or "-"
            item["pay_time"] = item["pay_time"] or "-"

            item["user"] = {
                "sn": user.get("sn", ""),
                "avatar": await UrlUtil.to_absolute_url(user.get("avatar", "")),
                "nickname": user.get("nickname", ""),
                "mobile": user.get("mobile", "")
            }

            item["pack"] = {
                "name": pack.get("name", ""),
                "description": pack.get("description", ""),
                "train_num": pack.get("train_num", ""),
                "robot_num": pack.get("robot_num", ""),
                "know_num": pack.get("know_num", ""),
                "calendar": pack.get("calendar", ""),
                "duration": pack.get("duration", ""),
                "give_points": pack.get("give_points", ""),
            }

            vo = TypeAdapter(schema.SubscribeListVo).validate_python(item)
            list_vo.append(vo)

        _pager.lists = list_vo
        _pager.extend = {
            "pay_way": PayEnum.get_pay_way_msg(True),
            "pay_status": PayEnum.get_pay_status_msg(True)
        }
        return _pager

    @classmethod
    async def refund(cls, order_id: int, admin_id: int):
        """
        订阅退款处理。

        Args:
            order_id: 订单ID
            admin_id: 管理ID

        Returns:
            None

        Author:
            zero
        """
        order = await SubscribeOrderModel.filter(id=order_id, is_delete=0).get()
        user = await UserModel.filter(is_delete=0, id=order.user_id).first()

        if not user:
            raise AppException("用户已不存在")

        if order.pay_status == PayEnum.PAID_NO:
            raise AppException("订单尚未支付")

        if order.refund_status == PayEnum.REFUND_OK:
            raise AppException("订单已退款了")

        if order.refund_status == PayEnum.REFUND_ING:
            raise AppException("订单退款中,请勿重复操作!")

        try:
            async with in_transaction("mysql"):
                # 查当前会员
                member = await (UserSubscribeModel
                                .filter(user_id=order.user_id)
                                .filter(package_id=order.package_id)
                                .filter(is_delete=0)
                                .first())

                if member:
                    snapshot = json.loads(order.snapshot)
                    calendar: str = snapshot["calendar"]
                    duration: int = snapshot["duration"]
                    is_perpetual: int = member.is_perpetual

                    # 计算时长
                    if calendar == "day":
                        dt = datetime.fromtimestamp(order.pay_time)
                        dt = dt + timedelta(days=duration)
                        package_ren_time = int(dt.timestamp())
                    elif calendar == "month":
                        dt = datetime.fromtimestamp(order.pay_time)
                        dt = dt + relativedelta(months=duration)
                        package_ren_time = int(dt.timestamp())
                    elif calendar == "year":
                        dt = datetime.fromtimestamp(order.pay_time)
                        dt = dt + relativedelta(years=duration)
                        package_ren_time = int(dt.timestamp())
                    elif calendar == "perpetual":
                        is_perpetual = 1

                    # 计算扣减后的到期时间
                    current_time = int(time.time())
                    vip_end_time = member.expire_time
                    add_ren_time = package_ren_time - order.pay_time
                    if member.expire_time > current_time:
                        vip_end_time = member.expire_time - add_ren_time
                        if vip_end_time < current_time:
                            vip_end_time = current_time

                    # 更新到期时间
                    await UserSubscribeModel.filter(id=member.id).update(
                        expire_time=vip_end_time,
                        is_perpetual=is_perpetual,
                        update_time=int(time.time())
                    )

                    # 查有效期记录
                    effective = await (UserSubscribeModel
                                       .filter(is_delete=0)
                                       .filter(user_id=order.user_id)
                                       .filter(expire_time__gt=current_time)
                                       .order_by("-effect_time")
                                       .all())

                    # 会员延迟生效
                    eff_end_time = 0
                    for index, eff in enumerate(effective):
                        if index == 0:
                            if eff.remain_time > 0:
                                eff.expire_time = eff.expire_time - eff.remain_time
                            if eff.effect_time > current_time:
                                eff.effect_time = current_time
                            eff_end_time = eff.expire_time
                            eff.remain_time = 0
                            await eff.save()
                            continue

                        if eff.is_perpetual:
                            break

                        if eff.remain_time > 0:
                            eff.effect_time = eff_end_time
                            eff.expire_time = eff_end_time + eff.remain_time
                        else:
                            remain_time = TimeUtil.compute_remain_time(eff.expire_time)
                            if remain_time > 0:
                                eff.effect_time = eff_end_time
                                eff.expire_time = eff_end_time + remain_time
                            else:
                                eff.effect_time = 0
                                eff.expire_time = eff_end_time

                        eff_end_time = eff.expire_time
                        eff.update_time = current_time
                        await eff.save()

                # 更新订单
                await SubscribeOrderModel.filter(id=order.id).update(
                    refund_status=PayEnum.REFUND_OK,
                    update_time=int(time.time())
                )

                # 发起退款
                if order.pay_way != PayEnum.WAY_BALANCE:
                    await RefundLogic.refund(
                        scene="subscribe",
                        order_id=order.id,
                        refund_amount=order.paid_amount,
                        admin_id=admin_id
                    )
        except Exception as e:
            raise AppException(str(e))
